package com.hzit.seckill.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.hzit.common.utils.IdWorker;
import com.hzit.seckill.dao.SeckillGoodsDao;
import com.hzit.seckill.dao.SeckillOrderDao;
import com.hzit.seckill.entity.SeckillStatus;
import com.hzit.seckill.entity.TbSeckillGoods;
import com.hzit.seckill.entity.TbSeckillOrder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 类名：
 * 作者：WF
 * 功能：
 */
@Component
@Slf4j
public class MultiThreadingCreateOrder {

	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private SeckillOrderDao orderDao;
	@Autowired
	private SeckillGoodsDao goodsDao;
	@Autowired
	private IdWorker idWorker;
	@Autowired
	private RedissonClient redissonClient;
	//1. 异步下单
	@Async
	public void fareOrder() {

		//1.1 取得左边放到的秒杀商品的状态对象,为了公平起见，先排队先购买
		String seckillOrderQueryStr = redisTemplate.boundListOps("SeckillOrderQueue").rightPop();
		//1.2 转换为对象
		SeckillStatus status = null;
		if(StrUtil.isNotBlank(seckillOrderQueryStr)){
			status = JSON.parseObject(seckillOrderQueryStr,SeckillStatus.class);
		}
		//1.3 得到商品编号
		Long secKillId = status.getSecKillId();
		//1.9 可以直接取一个信号量中的值
		RSemaphore semaphore = redissonClient.getSemaphore("seckill:semaphore" + secKillId);
		boolean b = semaphore.tryAcquire();
		if(b){

			//1.4 得到时间段
			String time = status.getTime();
			//1.5 为了避免超卖，我们在redis中放了一个list数组，我们每次下单购买时就从右边弹出一件，如果弹出的是null，证明没有商品了！
			String s = redisTemplate.boundListOps("SeckillGoodsCountList_" + secKillId).rightPop();
			if(s == null){
				//清除排队的数量与排队的状态
				clearQueue(status.getUsername());
				return;
			}

			//1.6 查询商品，解决商品销售中特殊情况
			BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps("seckillGoods_" + time);
			String seckillGoodsStr = operations.get(secKillId + "");
			TbSeckillGoods seckillGoods = null;
			if(StrUtil.isNotBlank(seckillGoodsStr)){
				seckillGoods = JSON.parseObject(seckillGoodsStr,TbSeckillGoods.class);
			}
			if(seckillGoods == null || seckillGoods.getStockCount() == 0){
				throw new RuntimeException("商品己卖完！");
			}
			//1.7 精准控制秒杀商品的库存量
			//1.7.1 将SeckillGoodsCount：商品id的值自增-1
			Long goodsCount = redisTemplate.boundHashOps("SeckillGoodsCount").increment(secKillId + "", -1);
			//1.7.2 将其重新设置回库存
			seckillGoods.setStockCount(new Integer(goodsCount + ""));

			//1.6 其它特殊情况处理
			if(seckillGoods.getStockCount() <= 0){
				//1.6.1 保存商品到数据库中
				goodsDao.updateById(seckillGoods);
				//1.6.2 从redis中清除此商品
				redisTemplate.boundHashOps("seckillGoods_" + status.getTime()).delete(secKillId + "");
			}else{  // 1.6.3 重新改回到redis中
				redisTemplate.boundHashOps("seckillGoods_" + status.getTime()).put(secKillId + "",JSON.toJSONString(seckillGoods));
			}

			//1.7. 下单
			TbSeckillOrder order = new TbSeckillOrder();
			long id = idWorker.nextId();
			order.setId(id);
			order.setCreateTime(new Date());
			order.setMoney(seckillGoods.getCostPrice());
			order.setSeckillId(new Integer(secKillId + "") );
			order.setStatus("0");
			order.setUserId(status.getUsername());
			//将订间放到redis中
			redisTemplate.opsForValue().set("seckill:order:" + status.getUsername(),JSON.toJSONString(order));

			//1.8 修改seckillStatus对象
			status.setMoney(seckillGoods.getCostPrice().floatValue());
			status.setOrderId(id);
			status.setStatus(2);      // 等待支付
			//1.8.1 重新保存回redis中
			redisTemplate.opsForValue().set("seckillStatus",JSON.toJSONString(status));

		}else{
			log.info("当前己超过最大流量限制！");
		}





	}
	//2. 清空排队信息
	private void clearQueue(String name) {
		//1. 清理排队的数量(避免重复排队)
		redisTemplate.boundHashOps("UserQueueCount").delete(name);
		//2. 清除排队的状态值
		redisTemplate.boundHashOps("UserQueueStatus").delete(name);
	}
}
